package parser;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import model.*;

/***
 * Interface you need to implement for creating a Parser
 * @author Federico
 *
 */
public class ZbjParser extends Parser{

	private BufferedInputStream bufferedInput = null;
	private final boolean DEBUG = false; // flag to remove or write debug messages
	private model.Class c;
	byte [] array = new byte[2];
	
	public ZbjParser(){
		super(new Model());
	}
	
	/***
	 * Method for read byte by byte and look for marks. At the end, a model will be created
	 */
	public void doParse(File file) {
		try {
			FileInputStream fileInput = new FileInputStream(file);
			bufferedInput = new BufferedInputStream(fileInput);
			
			byte[] read = new byte[1];
			int leidos = bufferedInput.read(read);
			while (leidos > 0)
			{
				array[0]=array[1];
				array[1]=read[0];
				procesaEtiqueta(printArray());
				
				leidos = bufferedInput.read(read);
			}
	
			bufferedInput.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/***
	 * Method which transforms 2 bytes in a String
	 * @return 2 bytes in String format
	 */
	private String printArray() {
		return Integer.toHexString(unsigned(array[0]))+Integer.toHexString(unsigned(array[1]));
	}
	
	/***
	 * Method for converting bytes to unsigned.
	 * @param b
	 * @return
	 */
	private int unsigned(byte b){
		return b & 0xFF;
	}
	
	/***
	 * Method which compares 2 bytes with the marks defined in Nemos class
	 * This marks are used for start object, start attribute, etc.
	 * @param n 2 bytes to compare with the marks
	 * @see Nemos
	 */
	private void procesaEtiqueta(String n){
		switch(n){
		case Nemos.startFile:
			if(DEBUG)
				System.out.println("Start File");
			break;
		case Nemos.startObject:
			if(DEBUG)
				System.out.println("\tStart object");
			createObject();
			break;
		case Nemos.startAttr:
			if(DEBUG)
				System.out.println("\t\tStart Attribute");
			addAttribute();
			break;
		case Nemos.startMethod:
			if(DEBUG)
				System.out.println("\t\tStart Method");
			createMethod();
			break;
		case Nemos.endObject:
			model.addClass(c);
			break;
		}
	}
	
	/***
	 * Method which creates a method(name, access and parameters)
	 */
	private void createMethod() {
		String name = readString();
		char access = name.charAt(0);
		name = name.substring(1);
		Method m = null;
		if(access == '+')
			m = new Method(name, 0);
		else if(access == '-') m = new Method(name, 1);
		
		byte b [] = new byte[1];
		try {
			bufferedInput.read(b);
			int aux = b[0];
			String param;
			while(aux>0){
				param = readString();
				m.addParameter(param);
				aux--;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(m!=null && c!=null){
			if(DEBUG)
				System.out.println("\t\t\t"+m);
			if(!m.getName().substring(0,2).equals("__"))
				c.addMethod(m);
		}
	}

	/***
	 * Method which creates an attribute (name and access)
	 */
	private void addAttribute() {
		String name = readString();
		char access = name.charAt(0);
		name = name.substring(1);
		Attribute a = null;
		if(access == '+')
			a = new Attribute(name, 0);
		else if(access == '-') a = new Attribute(name, 1);
		if(a!=null && c!=null){
			if(DEBUG)
				System.out.println("\t\t\t"+a);
			c.addAttribute(a);
		}
	}

	/***
	 * Method which creates an object (name and parent)
	 */
	private void createObject() {
		c = new model.Class();
		String name = readString();
		if(DEBUG)
			System.out.println("\tClass name: "+name);
		c.setName(name);
		String parent = readString();
		if(DEBUG)
			System.out.println("\tClass parent: "+parent);
		c.setParent(parent);
	}

	/***
	 * Method which reads byte by byte looking for a '0' to stop.
	 * All the bytes read are the string.
	 * @return The string between the start and the value before the '0'
	 */
	private String readString(){
		String toRet = "";
		byte b [] = new byte[1];
		b[0] = 1;
		try {
			do{
				bufferedInput.read(b);
				if((b[0])!=0)
					toRet += (char) b[0];
			}while(b[0]!=0);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return toRet;
	}
}
